// $Id: application.cpp 542 2008-06-04 14:41:30Z agjermun $
//
// ml278
// Copyright (C) 2008 Bjørn Øivind Bjørnsen, Aleksander Gjermundsen, Knut Andre Tidemann, Thomas Hansen
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

#include "planetaryscenemanager.h"
#include "application.h"
#include "spaceship.h"
#include "eventlistener.h"
#include "eventmanager.h"
#include "planetarydynamicsworld.h"
#include "extendedcamera.h"

using namespace ml278;
/**
 * full constructor
 * @param bufferedKeys true if the keyboard input device is buffered
 * @param bufferedMouse true if the mouse input device is buffered
 * @param bufferedJoy true if the joystick input device is buffered
*/
Application::Application(bool bufferedKeys, bool bufferedMouse, bool bufferedJoy) :
		EventListener(ml278::INPUT),
		mStatsOn(true), mNumScreenShots(0),
		mFiltering(TFO_ANISOTROPIC),	mAniso(8),
		mSceneDetailIndex(0), mDebugOverlay(0), mKeysOverlay(0),
		mInputManager(0), mMouse(0), mKeyboard(0), mJoy(0)
{
	mBufferedKeys = bufferedKeys;
	mBufferedMouse = bufferedMouse;
	mBufferedJoy = bufferedJoy;

	mRoot = NULL;

#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
	mResourcePath = macBundlePath() + "/Contents/Resources/";
#else
	mResourcePath = "";
#endif
	mDynamicsWorld = NULL;

	mKeyboardInputEnabled = true;
	mMouseInputEnabled = true;
	mJoystickInputEnabled = true;

	mDisplayCameraDetails = false;

	mKeysOn = false;
	mContinue = true;
	
	mManualCameraRotateSpeed = 0.05;
	cameraZoom = -20;
	cameraZoomMax = -10.0000;
	cameraZoomMin = -25.0000;

	mPersonalSpaceShip = NULL;
}

/**
 * destructor
*/
Application::~Application()
{
	// XXX: BEWARE OF ORDER OF DELETION

	// Remove all objects
	delete mPersonalSpaceShip;

	// Remove the class as a window listener
	WindowEventUtilities::removeWindowEventListener(mWindow, this);
	windowClosed(mWindow);

	// Clean up after physics engine
	if (mDynamicsWorld)
		exitPhysics();

	if (mRoot)
		delete mRoot;
}

/**
 * start the Application and rendering
*/
void Application::go()
{
	if (!setup())
		return;

	mRoot->startRendering();

	destroyScene();
}

/**
 * initialize the Application
 * @see go()
 * @return true if successful
*/
bool Application::setup()
{
	String pluginsPath;

	// only use plugins.cfg if not static
#ifndef OGRE_STATIC_LIB
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
	pluginsPath = mResourcePath + "plugins.cfg.win32";
#else
	pluginsPath = mResourcePath + "plugins.cfg";
#endif
#endif

	mRoot = new Ogre::Root(pluginsPath, mResourcePath + "ogre.cfg", mResourcePath + "Ogre.log");

	setupResources();

	if (!configure())
		return false;

	mPersonalSpaceShip = 0;
	mExCamera = 0;
	mCameraMode = 0;
	mKeyMgr = 0;

        mKeyMgr = new KeyBindingsManager("keys.cfg");
	chooseSceneManager();
	createCamera();
	createViewports();

	// Set default mipmap level (NB some APIs ignore this)
	TextureManager::getSingleton().setDefaultNumMipmaps(5);

	// Set default texture filter
	MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
	MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);

	// Create any resource listeners (for loading screens)
	createResourceListener();
	// Load resources
	loadResources();

	createOverlayKeys();

	if (!createEventHandler())
		return(false);

	mLoadingBar->setDescription("Initialising physics...");
	mLoadingBar->setComment("Initialising physics...");
	if (!initPhysics())
		return false;

	mLoadingBar->setNonScriptProgress(0.30);

	// Create the scene
	mLoadingBar->setDescription("Creating scene...");
	mLoadingBar->setComment("Creating scene...");
	createScene();
	mLoadingBar->setNonScriptProgress(1.0);

	// Close the loading bar
	mLoadingBar->hide();


	return true;
}

/**
 * initialises the window
 * @see setup()
 * @return true if successful
*/
bool Application::configure()
{
	if (mRoot->showConfigDialog())
	{
		mWindow = mRoot->initialise(true);
		return true;
	}
	else
		return false;
}

/**
 * set the SceneManager
 * @see setup()
*/
void Application::chooseSceneManager()
{
	mRoot->addSceneManagerFactory(new PlanetarySceneManagerFactory());
	mSceneMgr = mRoot->createSceneManager("PlanetarySceneManager", "SMInstance");
}

/**
 * create the camera to view the scene a first person angle (the player's POV)
 * @see setup()
*/
void Application::createCamera()
{
	mExCamera = (ExtendedCamera*)mSceneMgr->createCamera("ExtendedCamera");
}

/**
 * create the EventManager and add it as a framelistener
 * @see setup()
 * @return true if successful
*/
bool Application::createEventHandler()
{
	mEventMgr = new EventManager();

	// register the EventManager as a framelistener
	mRoot->addFrameListener(mEventMgr);

	using namespace OIS;

	mDebugOverlay = OverlayManager::getSingleton().getByName("Core/DebugOverlay");
	
	// Disable the Ogre logo
	OverlayElement* guiLogo = OverlayManager::getSingleton().getOverlayElement("Core/LogoPanel");
	guiLogo->hide();

	// Disable the input system if no input units are enabled
	if (mKeyboardInputEnabled || mMouseInputEnabled || mJoystickInputEnabled)
	{
		LogManager::getSingletonPtr()->logMessage("*** Initializing OIS ***");

		ParamList pl;
		size_t windowHnd = NULL;
		std::ostringstream windowHndStr;

		mWindow->getCustomAttribute("WINDOW", &windowHnd);
		windowHndStr << windowHnd;
		pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));

//#ifdef OIS_LINUX_PLATFORM
//		pl.insert(std::make_pair(std::string("x11_keyboard_grab"), std::string("false")));
//#endif

		mInputManager = InputManager::createInputSystem(pl);

		// Create input devices:
		if (mKeyboardInputEnabled)
		{
			mKeyboard = static_cast<Keyboard*>(mInputManager->createInputObject(OISKeyboard, mBufferedKeys));
			mKeyboard->setEventCallback(this);
		}
		if (mMouseInputEnabled)
		{
			mMouse = static_cast<Mouse*>(mInputManager->createInputObject(OISMouse, mBufferedMouse));
			mMouse->setEventCallback(this);
			LogManager::getSingletonPtr()->logMessage("Enabling mouse");
		}

		if (mJoystickInputEnabled)
		{
			// Test if we can get a joystick
			try
			{
				mJoy = static_cast<JoyStick*>(mInputManager->createInputObject(OISJoyStick, mBufferedJoy));
				mJoy->setEventCallback(this);
				LogManager::getSingletonPtr()->logMessage("Enabling joystick");
			}
			catch (OIS::Exception &e)
			{
				LogManager::getSingletonPtr()->logMessage(String("OIS::Exception caught, message: ") + e.eText);
				mJoy = NULL;
			}
			catch (...)
			{
				mJoy = NULL;
			}
		}
	}

	// Set initial mouse clipping size
	windowResized(mWindow);

	showDebugOverlay(true);

	// Register this class as a window listener
	WindowEventUtilities::addWindowEventListener(mWindow, this);
	showDebugOverlay(true);

	// add this object as an eventlistener
	mEventMgr->addEventListener(this);

	return true;
}

/**
 * creates the scene, this is where the various objects are put in the scene
 * @see setup()
*/
void Application::createScene()
{
	Light* pLight = mSceneMgr->createLight("TheSun");
	pLight->setType(Light::LT_DIRECTIONAL);
	pLight->setDirection(Vector3::UNIT_X);

	mSceneMgr->setSkyBox(true, "StarfieldSkyBox");
	mSceneMgr->setWorldGeometry("planet1.cfg");

	std::vector<PlanetaryBody*> bodies = static_cast<PlanetarySceneManager*>(mSceneMgr)->getPlanetaryBodies();
	static_cast<PlanetaryDynamicsWorld*>(mDynamicsWorld)->setBodies(bodies);

	mPersonalSpaceShip = new SpaceShip(mSceneMgr, mSceneMgr->getRootSceneNode(), mDynamicsWorld, "SpaceShip");
	// Turn the ship 180 degrees to face the planet
	btScalar turnAngle = Math::PI;
	mPersonalSpaceShip->turnAngleAroundYAxis(turnAngle);
	
	Vector3 spaceShipPos = Vector3(0, 10000, 10000);
	mPersonalSpaceShip->setPos(spaceShipPos);
	

	if (mPersonalSpaceShip)
	{
		mExCamera->setTarget((SceneNode*)mPersonalSpaceShip->getSceneNode());
		mExCamera->setDistanceToTarget(Vector3(0, -cameraZoom/4.0, cameraZoom));
		// Trick to make the spaceship correctly oriented, slightly rolls it
		btScalar rollForce = 0.0000000001;
		mPersonalSpaceShip->fireRollThruster(rollForce);
	}

	mGUI = new GUIMenu(this, mSceneMgr, (Camera*)mExCamera, mKeyMgr, true);
	// Enable instructions overlay on startup (for HVExpo)
	mKeysOn = true;
	showKeysOverlay(mKeysOn);
}

/**
 * clears the scene, placeholder atm
*/
void Application::destroyScene()
{
	// clear scene here?
}

/**
 * create the viewport (rendering area)
 * @see setup()
*/
void Application::createViewports()
{
	Viewport *vp = mWindow->addViewport((Camera*)mExCamera);
	vp->setBackgroundColour(ColourValue(0, 0, 0));

	mExCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));
}

/**
 * setup external resources
 * @see setup()
 * @return true if successful
*/
bool Application::setupResources()
{
	ConfigFile cf;

	cf.load(mResourcePath + "resources.cfg");

	ConfigFile::SectionIterator seci = cf.getSectionIterator();

	String secName, typeName, archName;
	while (seci.hasMoreElements())
	{
		secName = seci.peekNextKey();
		ConfigFile::SettingsMultiMap *settings = seci.getNext();
		ConfigFile::SettingsMultiMap::iterator i;
		for (i = settings->begin(); i != settings->end(); ++i)
		{
			typeName = i->first;
			archName = i->second;
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
			// OS X does not set the working directory relative to the app,
			// In order to make things portable on OS X we need to provide
			// the loading with it's own bundle path location
			ResourceGroupManager::getSingleton().addResourceLocation(
			    String(macBundlePath() + "/" + archName), typeName, secName);
#else
			ResourceGroupManager::getSingleton().addResourceLocation(
			    archName, typeName, secName);
#endif
		}
	}

	return(true);
}

/**
 * add spesific resources, initialises the "loading bar"
 * @see setup()
*/
void Application::createResourceListener()
{
	// Start QuickGUI script parser
	QuickGUI::registerScriptParser();

	// Make sure Core is loaded
	ResourceGroupManager::getSingleton().addResourceLocation("Media/packs/OgreCore.zip", "Zip", "Core");
	ResourceGroupManager::getSingleton().initialiseResourceGroup("Core");

	mLoadingBar = new LoadingBar(mWindow);
	mLoadingBar->init();
	ResourceGroupManager::getSingleton().addResourceGroupListener(mLoadingBar);
}

/**
 * load the resources
 * @pre all neccesary resources have been added
 * @see setup()
*/
void Application::loadResources()
{
	ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

// Previous framelistener functions

/**
 * called when the window is resized and checks for mouse events
 * @param rw the rendering window
*/
void Application::windowResized(RenderWindow *rw)
{
	unsigned int width, height, depth;
	int left, top;

	rw->getMetrics(width, height, depth, left, top);

	if (mMouse)
	{
		const OIS::MouseState &ms = mMouse->getMouseState();
		ms.width = width;
		ms.height = height;
	}
}

/**
 * called when the window is closed and cleans up
 * @param rw the rendering window
*/
void Application::windowClosed(RenderWindow *rw)
{
	// Remove OIS from this window
	if (rw == mWindow)
	{
		if (mInputManager && (mKeyboardInputEnabled || mMouseInputEnabled || mJoystickInputEnabled))
		{
			if (mKeyboardInputEnabled)
				mInputManager->destroyInputObject(mKeyboard);
			if (mMouseInputEnabled)
				mInputManager->destroyInputObject(mMouse);
			if (mJoystickInputEnabled)
				mInputManager->destroyInputObject(mJoy);

			OIS::InputManager::destroyInputSystem(mInputManager);
			mInputManager = NULL;
		}
	}
}

/**
 * enable/disable initialisation of keyboard input (run before go())
 * @param on true if keyboard input is to be enabled
*/
void Application::setKeyboardInput(bool on)
{
	mKeyboardInputEnabled = on;
}

/**
 * enable/disable initialisation of mouse input (run before go())
 * @param on true if mouse input is to be enabled
*/
void Application::setMouseInput(bool on)
{
	mMouseInputEnabled = on;
}

/**
 * enable/disable initialisation of joystick input (run before go())
 * @param on true if joystick input is to be enabled
*/
void Application::setJoystickInput(bool on)
{
	mJoystickInputEnabled = on;
}

/**
 * enable/disable display of the debug overlay
 * @param show true if the debug overlay is to be enabled
 * @see createFrameListener()
 * @see processUnbufferedKeyInput()
*/
void Application::showDebugOverlay(bool show)
{
	if (mDebugOverlay)
	{
		if (show)
			mDebugOverlay->show();
		else
			mDebugOverlay->hide();

	}
}

/**
 * enable/disable display of the key-bindings help overlay
 * @param show true if the key-bindings overlay is to enabled
 * @see processUnbufferedKeyInput()
*/
void Application::showKeysOverlay(bool show)
{
	if (mKeysOverlay)
	{
		if (show)
			mKeysOverlay->show();
		else
			mKeysOverlay->hide();
	}
}

/**
 * called at the beginning of each frame
 * @param evt the input/frame event
 * @return false if an event for closing the program has been recieved, otherwise true
*/
bool Application::frameStarted(const FrameEvent &evt)
{
	using namespace OIS;

	if (mGUI->isVisible())
		mGUI->injectTime(evt.timeSinceLastFrame);

	if (mWindow->isClosed())
		return false;

	if (mKeyboard)
		mKeyboard->capture();

	if (mMouse)
		mMouse->capture();

	// Update the physics world
	mDynamicsWorld->stepSimulation(evt.timeSinceLastFrame);

	if (mPersonalSpaceShip)
	{
		mPersonalSpaceShip->update(evt.timeSinceLastFrame);

		//if (mExCamera)
		//{
		//        switch (mCameraMode)
		//        {
		//                case 0: // 3rd person chase
		//                        mExCamera->update (evt.timeSinceLastFrame,
		//                        mPersonalSpaceShip->getCameraNode ()->getWorldPosition (),
		//                        mPersonalSpaceShip->getSightNode ()->getWorldPosition ());
		//                        break;
		//                case 1: // 1st person
		//                        mExCamera->update (evt.timeSinceLastFrame,
		//                        mPersonalSpaceShip->getWorldPosition (),
		//                        mPersonalSpaceShip->getSightNode ()->getWorldPosition ());
		//                        break;
		//        }
		//}
		if (mExCamera)
			mExCamera->update(evt.timeSinceLastFrame);
		
	}

	mPersonalSpaceShip->update(evt.timeSinceLastFrame);

	return mContinue;
}

/**
 * called after each frame, updates statistics
 * @param evt the frame/input event
 * @return true if the application is to continue
*/
bool Application::frameEnded(const FrameEvent &evt)
{
	// Print camera details
	if (mDisplayCameraDetails)
		mDebugText = "P: " + StringConverter::toString(mExCamera->getDerivedPosition()) +
		             " " + "O: " + StringConverter::toString(mExCamera->getDerivedOrientation());

	updateStats();
	return true;
}

/**
 * updates the statistics for FPS etc on the default overlay
 * @see framEnded()
*/
void Application::updateStats()
{
	static String currFps = "Current FPS: ";
	static String avgFps = "Average FPS: ";
	static String bestFps = "Best FPS: ";
	static String worstFps = "Worst FPS: ";
	static String tris = "Triangle Count: ";
	static String batches = "Batch Count: ";

	// update stats when necessary
	try
	{
		OverlayElement* guiAvg = OverlayManager::getSingleton().getOverlayElement("Core/AverageFps");
		OverlayElement* guiCurr = OverlayManager::getSingleton().getOverlayElement("Core/CurrFps");
		OverlayElement* guiBest = OverlayManager::getSingleton().getOverlayElement("Core/BestFps");
		OverlayElement* guiWorst = OverlayManager::getSingleton().getOverlayElement("Core/WorstFps");

		const RenderTarget::FrameStats& stats = mWindow->getStatistics();
		guiAvg->setCaption(avgFps + StringConverter::toString(stats.avgFPS));
		guiCurr->setCaption(currFps + StringConverter::toString(stats.lastFPS));
		guiBest->setCaption(bestFps + StringConverter::toString(stats.bestFPS)
		                    + " " + StringConverter::toString(stats.bestFrameTime) + " ms");
		guiWorst->setCaption(worstFps + StringConverter::toString(stats.worstFPS)
		                     + " " + StringConverter::toString(stats.worstFrameTime) + " ms");

		OverlayElement* guiTris = OverlayManager::getSingleton().getOverlayElement("Core/NumTris");
		guiTris->setCaption(tris + StringConverter::toString(stats.triangleCount));

		OverlayElement* guiBatches = OverlayManager::getSingleton().getOverlayElement("Core/NumBatches");
		guiBatches->setCaption(batches + StringConverter::toString(stats.batchCount));

		OverlayElement* guiDbg = OverlayManager::getSingleton().getOverlayElement("Core/DebugText");
		guiDbg->setCaption(mDebugText);
	}
	catch (...) { /* ignore */ }
}

/**
 * initialises physics (Bullet) in the application
 * @return true if successful
 * @see setup()
*/
bool Application::initPhysics()
{
	LogManager::getSingletonPtr()->logMessage("*** Initializing Bullet Physics ***");

	using namespace ml278;

	// create default configuration for memory and collision
	mCollisionConfiguration = new btDefaultCollisionConfiguration();

	// use the default dispatcher
	mDispatcher = new btCollisionDispatcher(mCollisionConfiguration);

	// setup colshapemanager
	mColShapeMgr = new ColShapeManager();

	// Set the min and max size of the world
	btVector3 worldAabbMin(-10000, -10000, -10000);
	btVector3 worldAabbMax(10000, 10000, 10000);
	mOverlappingPairCache = new btAxisSweep3(worldAabbMin, worldAabbMax, 4096); // TODO: Change value (max objects in physics world)

	// set the default constraint solver
	btSequentialImpulseConstraintSolver *sol = new btSequentialImpulseConstraintSolver;
	mSolver = sol;

	//mDynamicsWorld = new btDiscreteDynamicsWorld(mDispatcher,mOverlappingPairCache,mSolver,mCollisionConfiguration);
	mDynamicsWorld = new PlanetaryDynamicsWorld(mDispatcher, mOverlappingPairCache, mSolver, mCollisionConfiguration);

	mDynamicsWorld->setGravity(btVector3(0, 0, 0));

	// Uncomment to enable terrain collision - not working atm
	static_cast<PlanetarySceneManager*>(mSceneMgr)->setDynamicsWorld(mDynamicsWorld);

	LogManager::getSingletonPtr()->logMessage("Physics initialization done.");

	return true;
}

/**
 * shuts down and cleans up physics in the application
 * @see ~Application()
*/
void Application::exitPhysics()
{
	// Cleanup stuff

	for (int i = mDynamicsWorld->getNumCollisionObjects() - 1; i >= 0; i--)
	{
		btCollisionObject *obj = mDynamicsWorld->getCollisionObjectArray()[i];
		btRigidBody *body = btRigidBody::upcast(obj);

		if (body && body->getMotionState())
		{
			delete body->getMotionState();
		}

		mDynamicsWorld->removeCollisionObject(obj);
	}

	delete mDynamicsWorld;
	delete mSolver;
	delete mOverlappingPairCache;
	delete mDispatcher;
	delete mCollisionConfiguration;
}

/**
 * Handles keydowns from the keyboard
 * @param e keyboard event triggered
*/
bool Application::keyPressed(const OIS::KeyEvent &e)
{
	Vector3 spacepos(0, 600, 600);
	std::ostringstream ss;
	bool manualCamera = mExCamera->isManuallyControlled();

	if(e.key == mKeyMgr->getKey(ml278::KEY_QUIT))
	{
		exit();
	}
	else if(e.key == mKeyMgr->getKey(ml278::KEY_TOGGLE_GUI))
	{
		if (mGUI->isVisible())
		{
			mGUI->hide();
		}
		else
		{
			mGUI->show();
		}
	}
	else if(e.key == mKeyMgr->getKey(ml278::KEY_SCREENSHOT))
	{
		ss << "screenshot_" << ++mNumScreenShots << ".png";
		mWindow->writeContentsToFile(ss.str());
		mDebugText = "Saved: " + ss.str();
	}
	
	// Forward keys to GUI if inside the menu
	if (mGUI->isVisible())
	{
		mGUI->injectKeyDown(static_cast<QuickGUI::KeyCode>(e.key));
		mGUI->injectChar(e.text);
	}
	// Keys that only work outside the menu
	else
	{
		btScalar force;
                if(e.key == mKeyMgr->getKey(ml278::KEY_FORWARD))
		{
			
			if(!manualCamera)
			{
				mPersonalSpaceShip->applyMovementDir(SpaceShip::FORWARD);
			}
			else
			{
				mExCamera->move(ml278::FORWARD, true);
			}
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_BACKWARD))
		{
			if(!manualCamera)
			{
				mPersonalSpaceShip->applyMovementDir(SpaceShip::BACKWARD);
			}
			else
			{
				mExCamera->move(ml278::BACKWARD, true);	
			}
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_YAW_LEFT))
		{
			if(!manualCamera)
			{
				mPersonalSpaceShip->applyMovementDir(SpaceShip::YAW_LEFT);
			}
			else
			{
				mExCamera->move(ml278::STRAFE_LEFT, true);	
			}
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_YAW_RIGHT))
		{
			if(!manualCamera)
			{
				mPersonalSpaceShip->applyMovementDir(SpaceShip::YAW_RIGHT);
			}
			else
			{
				mExCamera->move(ml278::STRAFE_RIGHT, true);
			}
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_PITCH_UP))
		{
			mPersonalSpaceShip->applyMovementDir(SpaceShip::PITCH_UP);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_PITCH_DOWN))
		{
			mPersonalSpaceShip->applyMovementDir(SpaceShip::PITCH_DOWN);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_KEYS_OVERLAY))
		{
			mKeysOn = !mKeysOn;
			showKeysOverlay(mKeysOn);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_STATS_OVERLAY))
		{
			mStatsOn = !mStatsOn;
			showDebugOverlay(mStatsOn);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_SWITCH_TEXTURE_FILTERING))
		{
			setTextureFiltering(mFiltering);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_SWITCH_POLYGON_MODE))
		{
			mSceneDetailIndex = (mSceneDetailIndex + 1) % 3;
			setPolygonMode(mSceneDetailIndex);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_CAMERA_FIRST_PERSON))
		{
			setCameraMode(0);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_CAMERA_THIRD_PERSON))
		{
			setCameraMode(1);
		}
		else if(e.key == mKeyMgr->getKey(ml278::KEY_CAMERA_FREEROAM))
		{
			if(mExCamera->isManuallyControlled())
			{
				// Switch back to follow camera previously used
				if (mPersonalSpaceShip)
				{
					mExCamera->enableManualControl(false);
				}
			}
			else
			{
				mExCamera->enableManualControl(true);
			}
		}
	}


	return mContinue;

}

/**
 * Handles keyups from the keyboard
 * @param e keyboard event received
*/
bool Application::keyReleased(const OIS::KeyEvent &e)
{
	// Forward keys to GUI if inside the menu
	if (mGUI->isVisible())
		mGUI->injectKeyUp(static_cast<QuickGUI::KeyCode>(e.key));
	// Keys that only work outside the menu
	else 
	{
		if(mExCamera->isManuallyControlled())
		{
			if(e.key == mKeyMgr->getKey(ml278::KEY_FORWARD))
			{
				mExCamera->move(ml278::FORWARD, false);
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_BACKWARD))
			{
				mExCamera->move(ml278::BACKWARD, false);	
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_YAW_LEFT))
			{
				mExCamera->move(ml278::STRAFE_LEFT, false);	
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_YAW_RIGHT))
			{
				mExCamera->move(ml278::STRAFE_RIGHT, false);
			}
		}
		else
		{
			if(e.key == mKeyMgr->getKey(ml278::KEY_FORWARD))
			{
				mPersonalSpaceShip->removeMovementDir(SpaceShip::FORWARD);
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_BACKWARD))
			{
				mPersonalSpaceShip->removeMovementDir(SpaceShip::BACKWARD);
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_YAW_LEFT))
			{
				mPersonalSpaceShip->removeMovementDir(SpaceShip::YAW_LEFT);
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_YAW_RIGHT))
			{
				mPersonalSpaceShip->removeMovementDir(SpaceShip::YAW_RIGHT);
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_PITCH_UP))
			{
				mPersonalSpaceShip->removeMovementDir(SpaceShip::PITCH_UP);
			}
			else if(e.key == mKeyMgr->getKey(ml278::KEY_PITCH_DOWN))
			{
				mPersonalSpaceShip->removeMovementDir(SpaceShip::PITCH_DOWN);
			}
		}
	}
	return mContinue;
}

/**
 * Handles mouse move events
 * @param e mouse event recevied
*/
bool Application::mouseMoved(const OIS::MouseEvent &e)
{
	if (mGUI->isVisible())
	{
		mGUI->injectMouseMove(e.state.X.rel, e.state.Y.rel);
	}
	else if(mExCamera->isManuallyControlled())
	{
		mExCamera->manualYaw(Degree(e.state.X.rel * -mManualCameraRotateSpeed));
		mExCamera->manualPitch(Degree(e.state.Y.rel * -mManualCameraRotateSpeed));
	}
	else
	{
		btScalar rollForce = e.state.X.rel * 0.001;
		btScalar pitchForce = e.state.Y.rel * -0.001;
		mPersonalSpaceShip->fireRollThruster(rollForce);
		mPersonalSpaceShip->firePitchThruster(pitchForce);
		
		int scroll = (e.state.Z.rel / 120); //gives values of 1 or -1 pr tick.	
	
		//Zoom IN...
		if(scroll > 0)
		{
			if(cameraZoom < cameraZoomMax)
			{
				cameraZoom += e.state.Z.rel * 0.001;
				mExCamera->setDistanceToTarget(Vector3(0,-cameraZoom/4.0,cameraZoom));
			}
		}

		//Zoom OUT...
		else if(scroll < 0)
		{
			if(cameraZoom > cameraZoomMin)
			{
				cameraZoom += e.state.Z.rel * 0.001;
				mExCamera->setDistanceToTarget(Vector3(0,-cameraZoom/4.0,cameraZoom));
			}
		}
	}

	return true;
}

/**
 * Handles mousebutton down events
 * @param e mouse event received
 * @param id identifier of the button being pressed
*/
bool Application::mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
        if(mGUI->isVisible())
        {
                mGUI->injectMouseButtonDown((QuickGUI::MouseButtonID)id);
        }
        else
        {
                btScalar force(10.0);
                switch (id)
                {
                        case OIS::MB_Left:
                                mPersonalSpaceShip->fireMainThruster(force);
                                break;
                        case OIS::MB_Right:
                                mPersonalSpaceShip->fireReverseThruster(force);
                                break;
                        default:
                                break;
                }
        }
	return true;
}

/**
 * Handles mousebutton up events
 * @param e mouse event received
 * @param id identifier of the button being released
*/
bool Application::mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id)
{
        if(mGUI->isVisible())
        {
                mGUI->injectMouseButtonUp((QuickGUI::MouseButtonID)id);
        }
	return true;
}

/**
 * Handles Joystick POV-events
 * @param e joystick event received
 * @param pov axis pressed?
*/
bool Application::povMoved(const OIS::JoyStickEvent & e, int pov)
{
	return(true);
}

/**
 * Handles joystick Axis-events
 * @param e joystick event received
 * @param axis the axis moved
*/
bool Application::axisMoved(const OIS::JoyStickEvent & e, int axis)
{
	LogManager::getSingletonPtr()->logMessage("Joystick event received, axis moved: " + StringConverter::toString(axis));
	return(true);
}

/**
 * Handles joystick slider-events
 * @param e joystick event received
 * @param sliderID the id of the slider moved
 */
bool Application::sliderMoved(const OIS::JoyStickEvent & e, int sliderID)
{
	return(true);
}

/**
 * Handles joystick butten-down events
 * @param e joystick event received
 * @param button id of the button being pressed
 */
bool Application::buttonPressed(const OIS::JoyStickEvent & e, int button)
{
	return(true);
}

/**
 * Handles joystick button-up events
 * @param e joystick event received
 * @param button id of the button being released
 */
bool Application::buttonReleased(const OIS::JoyStickEvent & e, int button)
{
	return(true);
}

/**
 * creates the key-bindings help overlay
 * @see setup()
*/
void Application::createOverlayKeys()
{
	mOverlayMgr = Ogre::OverlayManager::getSingletonPtr();
	mKeysOverlay = mOverlayMgr->create("overlay1");
	mOverlayPanel = static_cast<Ogre::OverlayContainer*>(mOverlayMgr->createOverlayElement("Panel", "container1"));
	mOverlayPanel->setDimensions(1, 1);
	mOverlayPanel->setPosition(0, 0);
	mKeysOverlay->add2D(mOverlayPanel);
	//mKeysOverlay->show();
	//mKeysOverlay->hide();
	//
	addTextBox("txtGreeting", "Instructions:\nL - Enable/Disable this text.\nW,A,S,D - Move the ship/camera around.\nMouse - Aim/turn the ship\nC - Detach the camera.\nR - Drawing mode (wireframe etc.)\nEsc - Menu/quit.", 10, 10, 100, 20, Ogre::ColourValue::White);
}

/**
 * adds a text box to the screen as an overlay
 * @param ID the unique name of this textbox
 * @param text the text to be placed inside the text box
 * @param x x-position of the top-left corner of the text box
 * @param y y-position of the top-left corner of the text box
 * @param width width of the text box
 * @param height height of the text box
 * @param color the text color
 * @see createOverlayKeys()
*/
void Application::addTextBox(
    const std::string& ID,
    const std::string& text,
    Ogre::Real x, Ogre::Real y,
    Ogre::Real width, Ogre::Real height,
    const Ogre::ColourValue& color)
{
	Ogre::OverlayElement* textBox = mOverlayMgr->createOverlayElement("TextArea", ID);
	textBox->setDimensions(width, height);
	textBox->setMetricsMode(Ogre::GMM_PIXELS);
	textBox->setPosition(x, y);
	textBox->setWidth(width);
	textBox->setHeight(height);
	textBox->setParameter("font_name", "BlueHighway");
	textBox->setParameter("char_height", "16");
	textBox->setColour(color);

	textBox->setCaption(text);
	mOverlayPanel->addChild(textBox);
}

/**
 * remove a given text box
 * @param ID the unique name of the text box to be removed
 * @see addTextBox()
*/
void Application::removeTextBox(const std::string& ID)
{
	mOverlayPanel->removeChild(ID);
	mOverlayMgr->destroyOverlayElement(ID);
}

/**
 * sets new text content inside an existing text box
 * @param ID the unique name of the text box which to insert the new text
 * @param Text the new text
 * @see addTextBox()
*/
void Application::setText(const std::string& ID, const std::string& Text)
{
	Ogre::OverlayElement* textBox = mOverlayMgr->getOverlayElement(ID);
	textBox->setCaption(Text);
}

/**
 * Stops the application, causing it to clean-up and exit()
*/
void Application::exit()
{
        mContinue = false;
}

/**
 * Sets the default texture filter (bilinear, trilinear, anistropic - in order of quality)
 * @param opts The texture filtering to apply
*/
void Application::setTextureFiltering(Ogre::TextureFilterOptions opts)
{
	switch (opts)
	{
		case TFO_BILINEAR:
			mFiltering = TFO_TRILINEAR;
			mAniso = 1;
			break;
		case TFO_TRILINEAR:
			mFiltering = TFO_ANISOTROPIC;
			mAniso = 8;
			break;
		case TFO_ANISOTROPIC:
			mFiltering = TFO_BILINEAR;
			mAniso = 1;
			break;
		default: break;
	}
	MaterialManager::getSingleton().setDefaultTextureFiltering(mFiltering);
	MaterialManager::getSingleton().setDefaultAnisotropy(mAniso);

	showDebugOverlay(mStatsOn);
}

/**
 * sets the drawing mode of the application, either solid (default), wireframe or points.
 * @param mode integer defining which drawing mode to use. Must be between [0-2]. Other numbers do not have any effect.
 *		if mode is 0, solid drawing mode is used
 *		if mode is 1, wireframe drawing mode is used
 *		if mode is 2, point drawing mode is used
*/
void Application::setPolygonMode(int mode)
{
	switch (mode)
	{
		case 0 : mExCamera->setPolygonMode(PM_SOLID); break;
		case 1 : mExCamera->setPolygonMode(PM_WIREFRAME); break;
		case 2 : mExCamera->setPolygonMode(PM_POINTS); break;
	}
}

/**
 * sets the camera view mode, either in first person mode or third-person mode
 * @param mode integer defining which mode to use, must be between [0-1] to have an effect
 * 		if mode is 0, third person mode is used
 *		if mode is 1, first person mode is used
*/
void Application::setCameraMode(int mode)
{
	if(mPersonalSpaceShip)
	{
		// 3rd person
		if(mode == 0)
			mPersonalSpaceShip->setVisible(true);
		// 1st person
		else if(mode == 1)
			mPersonalSpaceShip->setVisible(false);
		mCameraMode = mode;
	}	
}

/**
 * Gives whether the keys overlay is enabled and visible
 * @returns true if the keys overlay is enabled and visible, false if not.
*/
bool Application::getKeysOverlay()
{
	if (mKeysOverlay && mKeysOverlay->isVisible())
		return true;
	else
		return false;
}

/** 
 * Gives whether the stats overlay is enabled and visible
 * @returns true if the stats overlay is enabled and visible, false if not.
*/
bool Application::getStatsOverlay()
{
	if (mDebugOverlay && mDebugOverlay->isVisible())
		return true;
	else
		return false;
}

/**
 * Returns the currently used texture filtering mode
 * @returns TFO_BILINEAR, TFO_TRILINEAR or TFO_ANISOTROPIC, depending on which one is currently in use
 * @see setTextureFiltering(Ogre::TextureFilterOptions opts)
*/
Ogre::TextureFilterOptions Application::getTextureFiltering()
{
	if(mFiltering == TFO_TRILINEAR && mAniso == 1)
		return TFO_BILINEAR;
	else if(mFiltering == TFO_ANISOTROPIC && mAniso == 8)
		return TFO_TRILINEAR;
	else
		return TFO_ANISOTROPIC;
}

/**
 * Returns the currently used drawing mode
 * @returns 0 if the solid drawing mode is active, 1 if the wireframe drawing mode is active,
 *		2 if the points drawing mode is active, -1 if none of them are used.
 * @see setPolygonMode(int mode)
*/
int Application::getPolygonMode()
{
	int polyMode = mExCamera->getPolygonMode();
	if(polyMode == PM_SOLID)
		return 0;
	else if(polyMode == PM_WIREFRAME)
		return 1;
	else if(polyMode == PM_POINTS)
		return 2;
	else
		return -1;
}

/**
 * Returns the currently used camera-mode
 * @returns 0 if the camera is set at third person mode, 1 if the camera is set at first person mode
 * @see setCameraMode(int mode)
*/
int Application::getCameraMode()
{
	return mCameraMode;
}


